/* Copyright (c) 2011 by Mani Brundha Kattamuri, Haritha Subramanyam
 *
 * Project Info:  http://code.google.com/p/andriod-travelapp-csc780/wiki/Design
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301,
 * USA.
 */
package com.example.TravelApp.search.common.helper;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Collections;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import com.example.TravelApp.constants.MapConstants;
import com.example.TravelApp.search.common.PlaceObject;
import com.google.android.maps.GeoPoint;
import android.location.Location;
import android.util.Log;

/**
 * This is a helper class that contains static methods to generate and sort the
 * options from the list of places returned by Google Places. It contains
 * functions that calculate the time taken to cover the route between two places
 * given by Google places. These inferences are necessary to categorize the
 * places and give two trip plans. This class also contains functions that
 * create list of places sorted and grouped based on time and rating to give the
 * two trip plans for advanced search.
 * 
 * @author Haritha Subramanyam, Mani Brundha
 * 
 */
public class PlacesDirectionHelper implements MapConstants {

	private static int timeToDestination;

	/**
	 * Uses Google Direction Services to get the time required to travel between
	 * two places by car.
	 * 
	 * @param start
	 *            - the start location
	 * @param dest
	 *            - destination location
	 * @return time - time in minutes taken to cover the distance
	 */
	public static float getTimeInfo(String start, String dest) {
		String url = getUrl(start, dest);

		String result = "";
		InputStream is = null;

		try {
			HttpClient httpclient = new DefaultHttpClient();
			HttpPost httppost = new HttpPost(url);
			HttpResponse response = httpclient.execute(httppost);
			HttpEntity entity = response.getEntity();
			is = entity.getContent();
		} catch (Exception e) {
			Log.e("log_tag", "Error in http conection" + e.toString());
		}

		try {
			BufferedReader reader = new BufferedReader(new InputStreamReader(
					is, "iso-8859-1"), 8);
			StringBuilder sb = new StringBuilder();
			String line = null;
			while ((line = reader.readLine()) != null) {
				sb.append(line + "\n");
			}
			is.close();
			result = sb.toString();

		} catch (Exception e) {
			Log.e("log_tag", "Error converting result " + e.toString());
		}

		return parseJSONForTime(result);

	}

	/**
	 * Parses the JSON response given by Google direction services and returns
	 * the actual time to reach the destination location
	 * 
	 * @param result
	 *            - Response String in JSON format
	 * @return timeToDestination - time taken to reach destination
	 */
	public static float parseJSONForTime(String result) {
		try {
			JSONObject rootObj = new JSONObject(result); // rootObj ist jetzt
															// ein dict
			JSONArray routes = (JSONArray) rootObj.get("routes");
			if (routes.length() < 1)
				Log.e("JSON_log_tag", "ERROR no route there");
			JSONObject firstRoute = routes.getJSONObject(0);
			JSONArray legs = (JSONArray) firstRoute.get("legs");
			if (legs.length() < 1)
				Log.e("JSON_log_tag", "ERROR no legs there");
			JSONObject firstLeg = legs.getJSONObject(0);
			JSONObject durationObject = (JSONObject) firstLeg.get("duration");

			// get the values distance in meters and time in seconds
			timeToDestination = (Integer) durationObject.get("value");

			return (timeToDestination / 60);

		} catch (JSONException e) {
			Log.e("log_tag", "JSON Exception " + e.toString());
		}
		return timeToDestination;
	}

	/**
	 * String Builder for Google Maps JSON will create the Link for the google
	 * maps query based on the entered data
	 * 
	 * @param fromAdress
	 *            - From address latitude and longitude
	 * @param toAdress
	 *            - to address latitude and longitude
	 * @return url - URL for Google Maps
	 */
	public static String getUrl(String fromAdress, String toAdress) {
		StringBuffer urlString = new StringBuffer();
		urlString.append(GOOGLE_MAPS_URL_FOR_DIST);
		urlString.append(fromAdress.toString());
		urlString.append(GOOGLE_MAPS_URL_FOR_DIST_DEST);
		urlString.append(toAdress.toString());
		urlString.append(GOOGLE_MAPS_URL_FOR_DIST_SENSOR);
		return urlString.toString();
	}

	/**
	 * Computes the time in hrs taken to reach the destination from the start
	 * location. The average speed here is assumed to be 35 miles/hour
	 * 
	 * @param p1
	 *            - Start Latitude and Longitude in string
	 * @param p2
	 *            - Destination Latitude and Longitude in string
	 * @return time - time in hours
	 */
	public static double getTimeBetweenPoints(String p1, String p2) {
		double time = 0;
		String[] param1 = p1.split(DELIMITER_COMMA);

		if (!p1.equals("null,null")) {
			Double dlongi = Double.parseDouble(param1[1]);
			Double dlat = Double.parseDouble(param1[0]);
			int ilat = (int) (dlat * 1E6);
			int ilong = (int) (dlongi * 1E6);
			GeoPoint gp1 = new GeoPoint(ilat, ilong);
			String[] param2 = p1.split(DELIMITER_COMMA);
			Double dlongi2 = Double.parseDouble(param2[1]);
			Double dlat2 = Double.parseDouble(param2[0]);
			int ilat2 = (int) (dlat2 * 1E6);
			int ilong2 = (int) (dlongi2 * 1E6);
			GeoPoint gp2 = new GeoPoint(ilat2, ilong2);

			time = (PlacesDirectionHelper.getDistanceInMiles(gp1, gp2))
					/ AVG_SPEED;
		}
		return time;
	}

	/**
	 * Computes the distance between two geo points considering the initial and
	 * final bearings.
	 * 
	 * @param p1
	 *            - Start Latitude and Longitude in string
	 * @param p2
	 *            - Destination Latitude and Longitude in string
	 * @return retDist - distance between two geopoints in miles
	 */
	public static double getDistanceInMiles(GeoPoint p1, GeoPoint p2) {
		double lat1 = ((double) p1.getLatitudeE6()) / 1e6;
		double lng1 = ((double) p1.getLongitudeE6()) / 1e6;
		double lat2 = ((double) p2.getLatitudeE6()) / 1e6;
		double lng2 = ((double) p2.getLongitudeE6()) / 1e6;
		float[] dist = new float[1];
		Location.distanceBetween(lat1, lng1, lat2, lng2, dist);
		double retDist = dist[0] * TO_MILES_CONST;
		return retDist;
	}

	/**
	 * Trip plan is constructed from the Array list of PlaceObjects such that
	 * the user gets to see the top rated places
	 * 
	 * @param placesList
	 *            - Array list of PlaceObjects constructed from the JSONObject
	 *            received
	 * @param time
	 *            - Time selected by the user
	 * @param latandlog
	 *            - Latitude and longitude of place selected by the user
	 * @return ArrayList of PlaceObject Trip plan is constructed such that the
	 *         user gets to see the top rated places
	 */
	@SuppressWarnings("unchecked")
	public static ArrayList<PlaceObject> optionByRating(
			ArrayList<PlaceObject> placesListRecieved, String time,
			String latandlog) {
		float totalTime = 0, timelimit;
		ArrayList<PlaceObject> placesList = new ArrayList<PlaceObject>();
		ArrayList<PlaceObject> sortedPlaceList = new ArrayList<PlaceObject>();
		timelimit = Integer.parseInt(time);
		placesList = (ArrayList<PlaceObject>) placesListRecieved.clone();
		// The list is sorted by rating
		Collections.sort(placesList);

		// New places are added while the time permits
		for (int i = 0; i < placesList.size(); i++) {
			// time to travel and time spent at a particular place are
			// considered
			totalTime = (float) (totalTime
					+ PlacesDirectionHelper.getTimeBetweenPoints(placesList
							.get(i).getLatitudeAndLongitude(), latandlog) + placesList
					.get(i).getIntrestTime());
			latandlog = placesList.get(i).getLatitudeAndLongitude();
			sortedPlaceList.add(placesList.get(i));
			// breaks if total time exceeds selected time
			if (totalTime >= timelimit) {
				break;
			}
		}
		// places are ordered according to optimal route
		sortedPlaceList = PlacesDirectionHelper.optionByTime(sortedPlaceList,
				time, latandlog);
		return sortedPlaceList;
	}

	/**
	 * Trip plan is constructed from the Array list of PlaceObjects such that
	 * the user gets to see max number of places
	 * 
	 * @param placesList
	 *            Array list of PlaceObjects constructed from the JSONObject
	 *            received
	 * @param timeLimitString
	 *            Time selected by the user
	 * @param latandlog
	 *            Latitude and longitude of place selected by the user
	 * @return ArrayList of PlaceObject Trip plan is constructed such that the
	 *         user gets to see max number of places
	 */
	public static ArrayList<PlaceObject> optionByTime(
			ArrayList<PlaceObject> placesList, String timeLimitString,
			String latandlog) {
		float totalTime = 0, timeLimit;
		int minIndex;
		float timeMin, tempTime;
		timeLimit = Integer.parseInt(timeLimitString);
		ArrayList<PlaceObject> sortedList = new ArrayList<PlaceObject>();
		// New places are added while the time permits and array is not empty
		while (totalTime < timeLimit && placesList.size() > 0) {
			timeMin = 9999;
			minIndex = -1;
			for (int i = 0; i < placesList.size(); i++) {
				// time to travel and time spent at a particular place are
				// considered
				tempTime = (float) PlacesDirectionHelper.getTimeBetweenPoints(
						placesList.get(i).getLatitudeAndLongitude(), latandlog)
						+ placesList.get(i).getIntrestTime();
				// finds the place that takes the minimum amount of time
				if (timeMin > tempTime) {
					timeMin = tempTime;
					minIndex = i;
				}
			}
			// the place is added to the list
			latandlog = placesList.get(minIndex).getLatitudeAndLongitude();
			sortedList.add(placesList.remove(minIndex));
			totalTime += timeMin;
		}
		return sortedList;
	}

}
